/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.icee.myth.server.social;

import com.icee.myth.server.social.message.SocialMessage;
import com.icee.myth.common.messageQueue.DBMessageQueue;
import com.icee.myth.config.MapConfig;
import com.icee.myth.protobuf.builder.ClientToMapBuilder;
import com.icee.myth.server.GameServer;
import com.icee.myth.server.actor.Human;
import com.icee.myth.server.message.dbMessage.builder.MapDBMessageBuilder;
import com.icee.myth.server.player.MapPlayer;
import com.icee.myth.server.social.message.AddRelationShipSocialMessage;
import com.icee.myth.server.social.message.GetInfoSocialMessage;
import com.icee.myth.server.social.message.RemoveRelationShipSocialMessage;
import com.icee.myth.log.GameLogger;
import com.icee.myth.log.message.FileDebugGameLogMessage;
import com.icee.myth.log.message.GameLogMessage.GameLogType;
import com.icee.myth.log.message.builder.GameLogMessageBuilder;
import com.icee.myth.protobuf.ExternalCommonProtocol.BriefPlayerProto;
import com.icee.myth.protobuf.ExternalCommonProtocol.BriefPlayersProto;
import com.icee.myth.protobuf.InternalCommonProtocol.DBRelationProto;
import com.icee.myth.server.notification.NotifySystem;
import com.icee.myth.server.notification.message.BeAddFriendNotificationMessage;
import com.icee.myth.utils.Consts;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 *
 * @author liuxianke
 */
public class Relation {
    public final int id;  // 好友信息所有者id
    public Human human;   // 玩家引用（在线时才有关联）(不能作为在线判断，因为上线时会异步从数据库加载好友关系)
    public final TreeSet<Integer> concerns = new TreeSet<Integer>();    // 关注对象
    public final TreeSet<Integer> followers = new TreeSet<Integer>();   // 被关注
    public final TreeMap<Integer, Long> friends = new TreeMap<Integer, Long>();     // 朋友(key为朋友id，值为允许助战时间)

    public boolean inited;  // 加载标志
    public boolean dirty;   // 脏标志(是否存盘)

    public long lastVisitTime;  // 最近访问时间
    public long addConcernCooldownTime;   // 以名字添加好友功能冷却时间

    public LinkedList<SocialMessage> messages;  // 缓存消息队列

    public Relation(int id) {
        this.id = id;
        inited = false;
        dirty = false;
        lastVisitTime = GameServer.INSTANCE.getCurrentTime();
    }

    public void init(DBRelationProto relationProto) {
        if (!inited) {
            inited = true;

            if (relationProto != null) {
                List<Integer> followerList = relationProto.getFollowersList();
                if (!followerList.isEmpty()) {
                    for (Integer followId : followerList) {
                        followers.add(followId);
                    }
                }

                List<Integer> concernProtos = relationProto.getConcernsList();
                if (!concernProtos.isEmpty()) {
                    for (Integer concern : concernProtos) {
                        concerns.add(concern);

                        if (followers.contains(concern)) {
                            friends.put(concern, 0L);
                        }
                    }
                }
            }

            // 注意: 默认“我的好友”组号为0号，不需要记入组列表中，其他组从1开始编号
            lastVisitTime = GameServer.INSTANCE.getCurrentTime();

            // 处理缓存消息
            handleMessages();
        } else {
            GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                    FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                    "Player["+id+"]'s Relation data multi-inited error."));
        }
    }

    public DBRelationProto buildDBRelationProto() {
        DBRelationProto.Builder builder1 = DBRelationProto.newBuilder();

        for (Integer concern : concerns) {
            builder1.addConcerns(concern);
        }

        if (!followers.isEmpty()) {
            builder1.addAllFollowers(followers);
        }

        return builder1.build();
    }

    public BriefPlayersProto buildRelationProto() {
        BriefPlayersProto.Builder builder1 = BriefPlayersProto.newBuilder();

        ArrayList<Integer> unknownConcerns = null;
        for (Integer concern : concerns) {
            BriefPlayerInfo playerInfo = GameServer.INSTANCE.briefPlayerInfos.get(concern);
            if (playerInfo != null) {
                boolean isFriend = friends.containsKey(playerInfo.id);
                long cooldown = isFriend?friends.get(playerInfo.id):0;
                builder1.addConcerns(playerInfo.buildBriefPlayerProto(isFriend, cooldown));
            } else{
                if (unknownConcerns == null) {
                    unknownConcerns = new ArrayList<Integer>();
                }
                unknownConcerns.add(concern);
            }
        }

        if (unknownConcerns != null) {
            // 请求数据库加载简略玩家列表
            DBMessageQueue.queue().offer(MapDBMessageBuilder.buildGetBriefPlayerInfosDBMessage(id, unknownConcerns));
        }

        return builder1.build();
    }

    public BriefPlayersProto buildBriefPlayersProto(BriefPlayerProto briefPlayerProto) {
        BriefPlayersProto.Builder builder = BriefPlayersProto.newBuilder();

        builder.addConcerns(briefPlayerProto);

        return builder.build();
    }

    public BriefPlayersProto buildBriefPlayersProto(LinkedList<BriefPlayerInfo> briefPlayerInfos) {
        BriefPlayersProto.Builder builder = BriefPlayersProto.newBuilder();

        for (BriefPlayerInfo briefPlayerInfo : briefPlayerInfos) {
            int playerId = briefPlayerInfo.id;

            if (concerns.contains(playerId)) {
                boolean isFriend = friends.containsKey(briefPlayerInfo.id);
                long cooldown = isFriend?friends.get(briefPlayerInfo.id):0;
                builder.addConcerns(briefPlayerInfo.buildBriefPlayerProto(isFriend, cooldown));
            } else {
                GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                        FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                        "Player["+id+"] build concerns proto error because can't find concern["+playerId+"]."));
            }
        }

        return builder.build();
    }

    public void addMessage(SocialMessage socialMessage) {
        if (messages == null) {
            messages = new LinkedList<SocialMessage>();
        }
        messages.add(socialMessage);
    }

    public void handleMessages() {
        if (messages != null) {
            for (SocialMessage socialMessage : messages) {
                switch (socialMessage.type) {
                    case SMT_GET_INFO: {
                        if (inited) {
                            // 若找到对应的在线玩家Human对象，设置其关系引用
                            MapPlayer player = GameServer.INSTANCE.players.get(((GetInfoSocialMessage)socialMessage).id);
                            if ((player != null) && (player.human != null) && (player.human.inGame)) {
                                player.human.setRelation(this);
                            }
                        } else {
                            GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                                    FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                                    "Player["+id+"] relation info not found when get info."));
                        }
                        break;
                    }
                    case SMT_ADD_RELATIONSHIP: {
                        AddRelationShipSocialMessage addRelationShipByIdSocialMessage = (AddRelationShipSocialMessage)socialMessage;
                        if (inited) {
                            GameServer.INSTANCE.social.addRelationShip(addRelationShipByIdSocialMessage.followerId, addRelationShipByIdSocialMessage.concernId);
                        } else {
                            // 若找到在线follower玩家，通知他concern目标不存在
                            MapPlayer player = GameServer.INSTANCE.players.get(addRelationShipByIdSocialMessage.followerId);
                            if ((player != null) && (player.human != null) && (player.human.inGame)) {
                                player.human.sendMessage(ClientToMapBuilder.buildAddConcernFail(addRelationShipByIdSocialMessage.concernId));
                            }

                            GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                                    FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                                    "Player["+id+"] relation info not found when add relationship."));
                        }
                        break;
                    }
                    case SMT_REMOVE_RELATIONSHIP: {
                        RemoveRelationShipSocialMessage removeRelationShipSocialMessage = (RemoveRelationShipSocialMessage)socialMessage;
                        if (inited) {
                            GameServer.INSTANCE.social.removeRelationShip(removeRelationShipSocialMessage.followerId, removeRelationShipSocialMessage.concernId);
                        } else {
                            GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                                    FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                                    "Player["+id+"] relation info not found when remove relationship."));
                        }
                        break;
                    }
                }
            }

            messages.clear();
            messages = null;
        }
    }

    // 注意：该方法是在关系双方的关系对象都在内存中找到且已初始化时才被调用
    public void addConcern(int concernId) {
        if (!concerns.contains(concernId)) {
            if (concerns.size() < MapConfig.INSTANCE.maxConcernNum) {
                concerns.add(concernId);

                if (followers.contains(concernId)) {
                    friends.put(concernId, 0L);
                }

                dirty = true;

                if (human != null) {
                    BriefPlayerInfo briefPlayerInfo = GameServer.INSTANCE.briefPlayerInfos.get(concernId);

                    if (briefPlayerInfo != null) {
                        boolean isFriend = friends.containsKey(briefPlayerInfo.id);
                        long cooldown = isFriend?friends.get(briefPlayerInfo.id):0;
                        human.sendMessage(ClientToMapBuilder.buildConcernsInfo(buildBriefPlayersProto(briefPlayerInfo.buildBriefPlayerProto(isFriend, cooldown))));
                    } else{
                        ArrayList<Integer> unknownConcerns = new ArrayList<Integer>();
                        unknownConcerns.add(concernId);

                        // 请求数据库加载简略玩家列表
                        DBMessageQueue.queue().offer(MapDBMessageBuilder.buildGetBriefPlayerInfosDBMessage(id, unknownConcerns));
                    }

                    // 通知玩家成功添加关注
                    human.sendMessage(ClientToMapBuilder.buildAddConcernSuccess(concernId));
                }
            } else {
                GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                        FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                        "Player["+id+"] multi-add concern["+concernId+"] because number limit."));
            }
        } else {
            GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                    FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                    "Player["+id+"] multi-add concern["+concernId+"]."));
        }
    }

    public void removeConcern(int concernId) {
        if (concerns.remove(concernId)) {
            dirty = true;

            if (friends.containsKey(concernId)) {
                friends.remove(concernId);
            }

            if (human != null) {
                human.sendMessage(ClientToMapBuilder.buildSocialRemoveConcern(concernId));
            }
        } else {
            GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                    FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                    "Player["+id+"] can't remove concern["+concernId+"] because not in concern list."));
        }
    }

    public void addFollower(int followerId) {
        if (followers.add(followerId)) {
            if (concerns.contains(followerId)) {
                friends.put(followerId, 0L);

                // 通知客户端关注对象成为好友
                if (human != null) {
                    BriefPlayerInfo briefPlayerInfo = GameServer.INSTANCE.briefPlayerInfos.get(followerId);
                    
                    if (briefPlayerInfo != null) {
                        human.sendMessage(ClientToMapBuilder.buildBecomeFriend(briefPlayerInfo.buildBriefPlayerProto(true, 0)));
                    } else {
                        GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                                FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                                "Player["+id+"] add follower["+followerId+"] be friend can't get brief player info."));
                    }
                }
            }

            dirty = true;
        } else {
            GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                    FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                    "Player["+id+"] multi-add follower["+followerId+"]."));
        }
    }

    public void removeFollower(int followerId) {
        if (followers.remove(followerId)) {
            dirty = true;

            if (friends.containsKey(followerId)) {
                friends.remove(followerId);

                // 通知客户端好友变关注
                if (human != null) {
                    human.sendMessage(ClientToMapBuilder.buildBecomeConcern(followerId));
                }
            }
        } else {
            GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                    FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                    "Player["+id+"] can't remove follower["+followerId+"] because not in followers."));
        }
    }

    public void tryAddConcern(int concernId) {
        long currentTime = GameServer.INSTANCE.getCurrentTime();
        if (currentTime >= addConcernCooldownTime) {
            addConcernCooldownTime = currentTime + Consts.MILSECOND_1SECOND;
            if (concernId != id) {
                if (!concerns.contains(concernId)) {
                    GameServer.INSTANCE.social.addRelationShip(id, concernId);
                    if (MapConfig.INSTANCE.useCYLog == true) {
                        
                        List<String> cyLogList = new ArrayList<String>();
                        cyLogList.add("behaviorMC");
                        cyLogList.add(String.valueOf(MapConfig.INSTANCE.gameId));
                        cyLogList.add("KDTY");
                        cyLogList.add(String.valueOf(human.id));
                        cyLogList.add(human.name);
                        cyLogList.add("");
                        cyLogList.add("AddConcernDBBehavior");
                        cyLogList.add(concernId + "");
                        cyLogList.add("");
                        cyLogList.add("");
                        cyLogList.add("");
                        cyLogList.add("");
                        cyLogList.add("");
                        GameLogger.getlogger().log(GameLogMessageBuilder.buildFileCYGameLogMessage(cyLogList, GameLogType.GAMELOGTYPE_CY_BEHAVIOR));
                    }

                    GameLogger.getlogger().log(GameLogMessageBuilder.buildAddConcernDBBehaviorGameLogMessage(id, concernId));
                } else {
                    GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                            FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                            "Player[" + id + "] can't add concern[" + concernId + "] because already concern."));
                }
            } else {
                GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                        FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                        "Player[" + id + "] can't add self as concern."));
            }
        } else {
            GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                    FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                    "Player[" + id + "] can't add concern[" + concernId + "] because not cooldown."));
        }
    }

    public void tryRemoveConcern(int concernId) {
        if (concerns.contains(concernId)) {
            GameServer.INSTANCE.social.removeRelationShip(id, concernId);
        } else {
            GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                    FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                    "Player[" + id + "] can't remove concern["+concernId+"] because target not concern."));
        }
    }

    public void sendConcernNote(int targetPlayerId) {
        if (concerns.contains(targetPlayerId)) {
            if (!followers.contains(targetPlayerId)) {
                BriefPlayerInfo briefPlayerInfo = GameServer.INSTANCE.briefPlayerInfos.get(human.id);
                NotifySystem.INSTANCE.sendNote(targetPlayerId, new BeAddFriendNotificationMessage(human.id, human.name, briefPlayerInfo.leaderCardId, briefPlayerInfo.leaderCardLevel), false);
            } else {
                GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                        FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                        "Player[" + id + "] can't sent concern note to player["+targetPlayerId+"] because already friend."));
            }
        } else {
            GameLogger.getlogger().log(GameLogMessageBuilder.buildFileDebugGameLogMessage(
                    FileDebugGameLogMessage.DebugLogType.DEBUGLOGTYPE_ERROR,
                    "Player[" + id + "] can't sent concern note to player["+targetPlayerId+"] because not concerned."));
        }
    }
}
